import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;

/**
 * The Class ModelProxy acts an intermediary between the View and the model that is on the other end of the network.
 * 
 * @author Jedd Haberstro (joh9446@rit.edu)
 */
public class ModelProxy implements ViewListener
{
	private Socket socket;
	private DataInputStream inputStream;
	private PrintStream outputStream;
	private ModelListener modelListener;

	/**
	 * Instantiates a new model proxy.
	 *
	 * @param socket the socket from which to receive model updates
	 */
	public ModelProxy(Socket s) {
		socket = s;
		try {
			inputStream = new DataInputStream(s.getInputStream());
			outputStream = new PrintStream(s.getOutputStream());
		}
		catch (IOException exception) {
			performIOError(exception);
		}
	}
	
	/**
	 * Attempt to join a game sessions.
	 * @param session - the name of the game session.
	 */
	@Override
	public void join(ViewProxy v, String session) {
		// TODO Auto-generated method stub
		outputStream.println("j " + session);
		outputStream.flush();
	}

	/**
	 * Notify the view listener that a game piece should be placed.
	 * @param x - the x location on the grid of the placed game piece.
	 * @param y - the y location on the grid of the placed game piece.
	 * @param color - the color of the player that placed the piece.
	 */
	@Override
	public void placePiece(int x, int y, ColorType color) {
		// TODO Auto-generated method stub
		outputStream.println("p " + x + " " + y + " " + color);
		outputStream.flush();
	}
	
	/**
	 * Set the model listener.
	 *
	 * @param ml the new model listener
	 */
	public void setModelListener(ModelListener ml) {
		modelListener = ml;
		Thread thread = new Thread() {
			
			/**
			 * Read a string from the socket until a specified character is reached.
			 * @param end - the character at which to stop (not included in the returned string).
			 * @return the read string
			 * @throws IOException
			 */
			private String readUntil(char end) throws IOException {
				String s = "";
				byte c = inputStream.readByte();
				while (true) {
					if (c == end) break;
					s += (char)c;
					c = inputStream.readByte();
				}
				return s;
			}
			
			/**
			 * Run the socket input main loop and forward events to the model listener.
			 */
			public void run() {
				try {
					while(true) {
						byte c = inputStream.readByte();
						if (c == 'j') {
							byte space = inputStream.readByte();
							if (space != ' ') continue;
							byte color = inputStream.readByte();
							if (color == 'b') {
								modelListener.joinSuccessful(ColorType.BLACK);
							}
							else if (color == 'w') {
								modelListener.joinSuccessful(ColorType.WHITE);
							}
						}
						else if (c == 'f') {
							modelListener.joinFailed();
						}
						else if (c == 'p') {
							byte space = inputStream.readByte();
							if (space != ' ') continue;
							int x = Integer.parseInt(readUntil(' '));
							int y = Integer.parseInt(readUntil(' '));
							byte color = inputStream.readByte();
							if (color == 'b') {
								modelListener.placePiece(x, y, ColorType.BLACK);
							}
							else if (color == 'w') {
								modelListener.placePiece(x, y, ColorType.WHITE);
							}
						}
						else if (c == 'g') {
							byte space = inputStream.readByte();
							if (space != ' ') continue;
							int gtype = inputStream.readByte() - '0';
							if (gtype >= 0 && gtype <= 2) {
								modelListener.gameOver(GameOverType.values()[gtype]);
								break;
							}
						}
					}
				}
				catch (IOException exception) {
					performIOError(exception);
				}
			}
		};
		thread.start();
	}

	/**
	 * Print an IO error message, the stack trace, and exit the program.
	 * @param exception - the exception that caused the error.
	 */
	private void performIOError(IOException exception) {
		System.err.println("IO error occurred. Aborting program.");
		exception.printStackTrace();
		try {
			socket.close();
		}
		catch (IOException e) { }
		
		System.exit(1);
	}

	@Override
	public void disconnected() {
		// TODO Auto-generated method stub
		
	}
}
